Cloudflare Workers Deployment
**Referenced Files in This Document** - [wrangler.jsonc](file://wrangler.jsonc) - [worker.js](file://worker.js) - [package.json](file://package.json) - [.eleventy.js](file://.eleventy.js) - [cloudflare-pages.toml](file://cloudflare-pages.toml) - [netlify.toml](file://netlify.toml) - [src/alliance-login.njk](file://src/alliance-login.njk) - [src/alliance-members.njk](file://src/alliance-members.njk) - [src/admin/index.html](file://src/admin/index.html) - [src/_data/site.json](file://src/_data/site.json) - [src/_data/allianceMembers.json](file://src/_data/allianceMembers.json) - [tina/config.ts](file://tina/config.ts)Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
Introduction
This document explains how to configure, deploy, and manage a Cloudflare Worker that serves a static site with member authentication and dynamic APIs. It covers:
- wrangler.jsonc configuration for account identification, worker script path, assets binding, compatibility flags, and KV namespace bindings
- Environment variables and secrets required by the worker
- Deployment from local development to production, including preview and release workflows
- The worker.js implementation: route handlers for member authentication, API endpoints, and dynamic content delivery
- Legacy Pages compatibility configuration and migration rationale
- Practical deployment commands, environment-specific configuration tips, and troubleshooting
- Performance optimization, cold start mitigation, resource limits, and static asset delivery via Cloudflare’s edge network
Project Structure
The project builds a static site with Eleventy and deploys it via a Cloudflare Worker that exposes:
- Member authentication routes (/alliance/login, /alliance/verify, /alliance/logout, /alliance/members)
- Public static assets via the ASSETS binding
- Dynamic APIs (/api/polling.json, /api/auth, /api/auth/callback)
- Optional legacy Pages configuration for historical reference
graph TB
Dev["Developer Machine"] --> Build["Eleventy Build<br/>_site output"]
Build --> Worker["Cloudflare Worker<br/>worker.js"]
Worker --> KV["KV Namespaces<br/>MEMBER_EMAILS, MAGIC_TOKENS"]
Worker --> Secrets["Secrets<br/>SESSION_SECRET, RESEND_API_KEY,<br/>GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET"]
Worker --> Assets["ASSETS Binding<br/>_site directory"]
Assets --> Edge["Cloudflare Edge Network"]
Edge --> Browser["Browser"]
Worker --> External["External Services<br/>Resend, GitHub OAuth, Google Sheets"]
Diagram sources
- [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
- [worker.js:301-320](file://worker.js#L301-L320)
- [.eleventy.js:267-282](file://.eleventy.js#L267-L282)
Section sources
- [.eleventy.js:267-282](file://.eleventy.js#L267-L282)
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
- [package.json:5-12](file://package.json#L5-L12)
Core Components
- wrangler.jsonc: Defines worker identity, compatibility date, main module, observability, assets binding, compatibility flags, and placeholders for KV namespaces and secrets.
- worker.js: Implements routing, member authentication (magic-link login), session management, GitHub OAuth for CMS, and a live polling API backed by Google Sheets.
- Eleventy configuration: Controls build outputs and passthrough copies for static assets.
- Legacy Pages config: Retained for historical reference; canonical deployment is via Workers Assets.
Section sources
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
- [worker.js:1-321](file://worker.js#L1-L321)
- [.eleventy.js:1-283](file://.eleventy.js#L1-L283)
- [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
Architecture Overview
The worker acts as a router and orchestrator:
- Static routes are served via the ASSETS binding
- Member-authenticated routes are gated by session cookies validated against a shared secret
- Magic-link login uses two KV namespaces: one for approved emails and another for short-lived tokens
- Dynamic endpoints include a CORS-enabled OAuth handshake for CMS and a cached polling API
sequenceDiagram
participant U as "User Agent"
participant W as "Worker (worker.js)"
participant K1 as "KV : MEMBER_EMAILS"
participant K2 as "KV : MAGIC_TOKENS"
participant R as "Resend API"
participant G as "Google Sheets API"
U->>W : GET /alliance/members/*
W->>W : Read cookie and verify signature
alt Cookie missing/expired
W-->>U : 302 Redirect to /alliance/login?next=...
else Valid session
W->>W : Fetch from ASSETS
W-->>U : 200 Static content
end
U->>W : POST /alliance/login/ {email,_gotcha}
W->>K1 : Lookup member : <email>
alt Approved
W->>K2 : Store token : <hex>=<email>, TTL=900s
W->>R : Send magic-link email
else Not approved
W->>R : Optionally skip sending
end
W-->>U : 302 Redirect to /alliance/login?sent=1
U->>W : GET /alliance/verify?token=<hex>
W->>K2 : Retrieve token : <hex>
alt Exists
W->>K2 : Delete token : <hex>
W-->>U : 302 Redirect to /alliance/members/ with session cookie
else Expired/Invalid
W-->>U : 302 Redirect to /alliance/login?error=expired
end
U->>W : GET /api/polling.json?state=(federal|sa)
W->>G : Fetch spreadsheet data
G-->>W : JSON values
W-->>U : JSON with cache headers
Diagram sources
- [worker.js:77-177](file://worker.js#L77-L177)
- [worker.js:233-276](file://worker.js#L233-L276)
Detailed Component Analysis
wrangler.jsonc Configuration
- Identity and compatibility
- name: worker identifier used by Wrangler
- compatibility_date: ensures runtime behavior stability
- compatibility_flags: enables Node.js compatibility mode
- Main module
- main: path to the worker entrypoint
- Observability
- enabled: toggles OpenTelemetry-like observability
- Assets binding
- directory: static site output directory
- binding: variable name used in worker to serve assets
- KV namespaces and secrets
- KV namespaces: bind MEMBER_EMAILS and MAGIC_TOKENS
- Secrets: SESSION_SECRET, RESEND_API_KEY, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
Operational notes:
- KV namespaces and secrets are commented out as placeholders; uncomment and populate after creation
- The assets binding allows serving prebuilt static content from _site
Section sources
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
worker.js Implementation
Highlights:
- Session management
- Cookie name, duration, and HMAC-signed payload
- Constant-time verification to prevent timing attacks
- Member authentication
- POST /alliance/login/: validates email, checks membership in KV, stores ephemeral token, sends email via Resend
- GET /alliance/verify: consumes token, deletes it, sets session cookie
- GET /alliance/logout: clears session cookie
- GET /alliance/members/*: enforces session gate
- GitHub OAuth for CMS
- OPTIONS preflight for CORS
- GET /api/auth: redirects to GitHub OAuth
- GET /api/auth/callback: exchanges code for access token and posts it to opener window
- Live polling API
- GET /api/polling.json: reads Google Sheets values and returns structured JSON with cache headers
Routing logic:
- Match paths and methods, otherwise delegate to ASSETS
Security and correctness:
- KV namespaces must be configured; otherwise returns 503
- Secret values must be set; otherwise endpoints fail gracefully
- Token TTL and session duration are enforced
Section sources
- [worker.js:12-58](file://worker.js#L12-L58)
- [worker.js:77-177](file://worker.js#L77-L177)
- [worker.js:183-227](file://worker.js#L183-L227)
- [worker.js:233-276](file://worker.js#L233-L276)
- [worker.js:282-295](file://worker.js#L282-L295)
- [worker.js:301-320](file://worker.js#L301-L320)
Frontend Templates and Data
- Login page (/alliance/login/)
- Renders a form posting to /alliance/login/
- Shows “sent” view and error messages based on query params
- Members portal (/alliance/members/)
- Uses data from allianceMembers.json for dashboard tiles and resources
- Admin page
- Loads Sveltia CMS client for content editing
These templates are generated during Eleventy build and served via ASSETS.
Section sources
- [src/alliance-login.njk:1-73](file://src/alliance-login.njk#L1-L73)
- [src/alliance-members.njk:1-58](file://src/alliance-members.njk#L1-L58)
- [src/admin/index.html:1-12](file://src/admin/index.html#L1-L12)
- [src/_data/allianceMembers.json:1-40](file://src/_data/allianceMembers.json#L1-L40)
Legacy Pages Compatibility
- cloudflare-pages.toml: Deprecated; kept for historical reference
- netlify.toml: Alternative hosting configuration; not used for Workers deployment
Recommendation:
- Prefer wrangler.jsonc for current deployment; keep Pages config only for reference.
Section sources
- [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
- [netlify.toml:1-26](file://netlify.toml#L1-L26)
Dependency Analysis
- Build pipeline
- Eleventy generates _site from src
- Passthrough copies assets, admin, robots, and favicon
- Worker dependencies
- ASSETS binding depends on _site output
- KV namespaces depend on wrangler.jsonc bindings
- Secrets depend on environment configuration
- External integrations
- Resend for email delivery
- GitHub OAuth for CMS
- Google Sheets for polling data
graph LR
Eleventy[".eleventy.js<br/>Build _site"] --> Site["_site"]
Site --> WorkerJS["worker.js"]
WorkerJS --> AssetsBind["ASSETS binding"]
WorkerJS --> KV["KV Namespaces"]
WorkerJS --> Secrets["Secrets"]
WorkerJS --> Ext1["Resend API"]
WorkerJS --> Ext2["GitHub OAuth"]
WorkerJS --> Ext3["Google Sheets"]
Diagram sources
- [.eleventy.js:7-14](file://.eleventy.js#L7-L14)
- [wrangler.jsonc:9-15](file://wrangler.jsonc#L9-L15)
- [worker.js:301-320](file://worker.js#L301-L320)
Section sources
- [.eleventy.js:1-283](file://.eleventy.js#L1-L283)
- [wrangler.jsonc:1-35](file://wrangler.jsonc#L1-L35)
- [worker.js:1-321](file://worker.js#L1-L321)
Performance Considerations
- Cold start mitigation
- Keep worker.js minimal and avoid heavy initialization
- Use KV for small, fast lookups; cache long-lived secrets in environment
- Serve static assets via ASSETS binding to leverage edge caching
- Resource limits
- Respect CPU and memory quotas; avoid synchronous blocking operations
- Use streaming responses for large payloads when possible
- Caching strategy
- Polling API returns cache headers to reduce load
- Static assets benefit from immutable cache headers via build pipeline
- Observability
- Enable observability in wrangler.jsonc to monitor latency and errors
[No sources needed since this section provides general guidance]
Troubleshooting Guide
Common issues and resolutions:
- KV namespaces not configured
- Symptom: 503 responses from member routes
- Action: Create KV namespaces and update wrangler.jsonc bindings
- Missing secrets
- Symptom: OAuth or email endpoints fail
- Action: Set secrets via Wrangler CLI
- Invalid or expired magic link
- Symptom: Redirects to login with error query param
- Action: Ensure token TTL and KV deletion occur correctly
- CORS errors for OAuth callback
- Symptom: Cross-origin postMessage blocked
- Action: Verify preflight handling and origin constraints
- Static assets not loading
- Symptom: Blank pages or missing styles/scripts
- Action: Confirm Eleventy build completes and ASSETS binding matches _site
Section sources
- [worker.js:70-75](file://worker.js#L70-L75)
- [worker.js:153-177](file://worker.js#L153-L177)
- [worker.js:183-227](file://worker.js#L183-L227)
- [worker.js:301-320](file://worker.js#L301-L320)
Conclusion
This project demonstrates a robust Cloudflare Workers deployment model:
- Static site built by Eleventy and served via ASSETS
- Member authentication powered by KV-backed magic links and signed sessions
- Dynamic endpoints for CMS OAuth and live polling data
- Clear separation of concerns and environment-driven configuration
Adopt the provided deployment commands, configure KV and secrets, and rely on the ASSETS binding for optimal edge delivery.
[No sources needed since this section summarizes without analyzing specific files]
Appendices
Deployment Commands
- Local preview
- Build and run dev server: see scripts
- Production deployment
- Build and deploy: see scripts
Environment variables and secrets:
- SESSION_SECRET: random secret for HMAC signing
- RESEND_API_KEY: for sending magic-link emails
- GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET: for CMS OAuth
- GOOGLE_SHEETS_ID, GOOGLE_SHEETS_API_KEY: for polling API
KV namespaces:
- MEMBER_EMAILS: key format member:
- MAGIC_TOKENS: key format token:
, TTL 900 seconds
Section sources
- [package.json:5-12](file://package.json#L5-L12)
- [worker.js:4-11](file://worker.js#L4-L11)
- [wrangler.jsonc:17-34](file://wrangler.jsonc#L17-L34)
Environment-Specific Configuration Tips
- Use separate KV namespaces per environment (dev/stage/prod)
- Store secrets in environment variables; never commit to source
- Align compatibility_date with tested runtime behavior
- Validate CORS origins and preflight handling for OAuth
Section sources
- [worker.js:183-191](file://worker.js#L183-L191)
- [wrangler.jsonc:3-5](file://wrangler.jsonc#L3-L5)
Static Asset Delivery Through Cloudflare Edge
- Eleventy passthrough copies assets and admin folders
- ASSETS binding serves _site directory from edge locations
- Immutable cache headers for assets improve performance
Section sources
- [.eleventy.js:7-14](file://.eleventy.js#L7-L14)
- [wrangler.jsonc:9-12](file://wrangler.jsonc#L9-L12)